package com.iflytek.rule.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.iflytek.rule.cache.RedisConfig;
import com.iflytek.rule.cache.RedisUtils;
import com.iflytek.rule.dao.RuleMerchantDao;
import com.iflytek.rule.dao.RuleRequestDao;
import com.iflytek.rule.dao.RuleResultDao;
import com.iflytek.rule.enums.CodeEnums;
import com.iflytek.rule.exceptions.BusinessException;
import com.iflytek.rule.model.RuleMerchantModel;
import com.iflytek.rule.model.RuleRequestModel;
import com.iflytek.rule.model.RuleResultModel;
import com.iflytek.rule.service.IBusinessService;
import com.iflytek.rule.service.IRequestService;
import com.iflytek.rule.strategy.ResultTypeStrategy;
import com.iflytek.rule.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import static java.util.stream.Collectors.toList;


/**
 * @Author weibi
 * @Date 2020/3/3 10:41
 * @Version 1.0
 * @Description TODO
 **/
public class BusinessServiceImpl implements IBusinessService {


    private final static Logger log = LoggerFactory.getLogger(BusinessServiceImpl.class);

    @Autowired
    private IRequestService requestService;

    @Autowired
    private RuleRequestDao ruleRequestDao;

    @Autowired
    private RuleMerchantDao ruleMerchantDao;

    @Autowired
    private RuleResultDao ruleResultDao;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private RedisConfig redisConfig;

    @Override
    public Object operator(Object requestObj, // 入参对象
                           String resObjPath, // 出参对象
                           String merchant,
                           String xfRequestMethod) {

        Optional.ofNullable(requestObj).orElseThrow(() ->
                new BusinessException(CodeEnums.NULL_OBJECT.getCode(), "入参对象不能为空"));
        Optional.ofNullable(resObjPath).orElseThrow(() ->
                new BusinessException(CodeEnums.NULL_OBJECT.getCode(), "出参对象不能为空"));
        Optional.ofNullable(merchant).orElseThrow(() ->
                new BusinessException(CodeEnums.NULL_PARAM.getCode(), "merchant 不能为空"));
        Optional.ofNullable(xfRequestMethod).orElseThrow(() ->
                new BusinessException(CodeEnums.NULL_PARAM.getCode(), "xfRequestMethod 不能为空"));

        // 获取hospital信息
        RuleMerchantModel merchantModel = getRulemerchantModel(merchant);
        // 获取请求参数
        List<RuleRequestModel> requestModels = getRuleRequestModels(merchant, xfRequestMethod);
        List<CompletableFuture<Object>> futures = requestModels.stream()
                .map(requestModel -> CompletableFuture.supplyAsync(
                        () -> getResult(merchant,
                                xfRequestMethod,
                                requestObj,
                                requestModel,
                                merchantModel,
                                resObjPath))
                ).collect(toList());
        return futures.stream()
                .map(CompletableFuture::join)
                .collect(toList());

    }

    private Object getResult(String merchant,
                             String xfRequestMethod,
                             Object requestObj,
                             RuleRequestModel requestModel,
                             RuleMerchantModel merchantModel,
                             String resObjPath) {
        RuleResultModel resultModel = getRuleResultModel(merchant, xfRequestMethod);


        // 统一转string
        String requestParam = (String) requestService.requestParam(requestObj,
                merchant,
                xfRequestMethod,
                requestModel);

        String requestParamCommon = requestModel.getRequestParamCommon();
        if (!StrUtil.isBlank(requestParamCommon)) {
            requestParam = requestParamCommon.replace(" requestParam", requestParam);
        }

        String res = "";
        if (StrUtil.isBlank(requestModel.getRpcMethod())) {
//            // 走内部
//            res = (String) ServerTypeStrategy.getInstance().rpc(merchantModel,
//                    requestModel,
//                    requestParam);

        } else {
            // 自定义的rpc 需要接收 url 和 入参 两个变量
            String rpcStr = requestModel.getRpcMethod() +
                    "#" + merchantModel.getServerUrl() +
                    "#" + requestParam;
            res = rpc(rpcStr);
        }

        if (StrUtil.isBlank(res)) {
            throw new BusinessException(CodeEnums.RESULT_DATA_ERROR);
        }

        // 判断res 是json 还是 xml
        String resultType = Utils.getStrType(res);
        Object resObj = ResultTypeStrategy.getInstance().result(resultType,
                requestObj,
                res,
                requestModel.getXfRequestMethod(),
                resObjPath,
                resultModel);


        return resObj;
    }

    /**
     * hospital 配置信息
     *
     * @param merchant
     * @return
     */
    private RuleMerchantModel getRulemerchantModel(String merchant) {
        RuleMerchantModel merchantModel =
                (RuleMerchantModel) redisUtils.get("rule-engine:" + merchant);
        if (ObjectUtil.isEmpty(merchantModel)) {
            merchantModel = ruleMerchantDao.findByMerchant(merchant);
            Optional.ofNullable(merchantModel).orElseThrow(() ->
                    new BusinessException(CodeEnums.NULL_PARAM.getCode(), "您需要在表rule_merchant配置相关信息"));
            redisUtils.set("rule-engine:" + merchant, merchantModel, redisConfig.getTimeout().get("merchant"));
        }
        return merchantModel;
    }

    /**
     * 请求入参配置信息
     *
     * @param merchant
     * @param xfRequestMethod
     * @return
     */
    private List<RuleRequestModel> getRuleRequestModels(String merchant, String xfRequestMethod) {
        List<RuleRequestModel> ruleRequestModels = (List<RuleRequestModel>) redisUtils.get(Utils.setRedisKey(merchant, xfRequestMethod, "request"));
        if (CollectionUtil.isEmpty(ruleRequestModels)) {
            ruleRequestModels = ruleRequestDao.findByParam(merchant, xfRequestMethod);
            Optional.ofNullable(ruleRequestModels).orElseThrow(() ->
                    new BusinessException(CodeEnums.NULL_PARAM.getCode(), "您需要在表rule_request配置相关信息"));
            redisUtils.set(Utils.setRedisKey(merchant, xfRequestMethod, "request"), ruleRequestModels, redisConfig.getTimeout().get("request"));
        }
        return ruleRequestModels;
    }

    /**
     * 结果配置信息
     *
     * @param merchant
     * @param xfRequestMethod
     * @return
     */
    private RuleResultModel getRuleResultModel(String merchant, String xfRequestMethod) {
        RuleResultModel resultModel =
                (RuleResultModel) redisUtils.get(Utils.setRedisKey(merchant, xfRequestMethod, "result"));
        if (ObjectUtil.isEmpty(resultModel)) {
            resultModel = ruleResultDao.findByParam(merchant, xfRequestMethod);
            Optional.ofNullable(resultModel).orElseThrow(() ->
                    new BusinessException(CodeEnums.NULL_PARAM.getCode(), "您需要在表rule_result配置相关信息"));
            redisUtils.set(Utils.setRedisKey(merchant, xfRequestMethod, "result"), resultModel, redisConfig.getTimeout().get("result"));
        }
        return resultModel;
    }

    /**
     * 执行外部方法
     *
     * @param rpc
     * @return
     */
    private String rpc(String rpc) {
        try {
            String[] arr = rpc.split("\\#");
            Class clazz = Class.forName(arr[1]);

            if (arr.length == 3) {
                return ReflectUtil.invoke(clazz.newInstance(), arr[2]);
            } else {
                String[] strArr = new String[2];
                strArr[0] = arr[3];
                strArr[1] = arr[4];
                return ReflectUtil.invoke(clazz.newInstance(), arr[2], strArr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
